package main

import (
	"crypto/tls"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"strings"
	"time"
)

var reqClient *http.Client

// getRandStr 获得随机步数 str
func getRandStr(min, max int64) string {
	var res int64
	if min >= max || min == 0 || max == 0 {
		res = max
	}
	res = rand.Int63n(max-min) + min

	return fmt.Sprintf("%d", res)
}

// getUserAuthCode 获得access
func getUserAuthCode(user string, password string) (code string, err error) {
	requestUrl := "https://api-user.huami.com/registrations/+86" + user + "/tokens"
	DataUrlVal := url.Values{}
	DataUrlVal.Add("client_id", "HuaMi")
	DataUrlVal.Add("password", password)
	DataUrlVal.Add("redirect_uri", "https://s3-us-west-2.amazonaws.com/hm-registration/successsignin.html")
	DataUrlVal.Add("token", "access")

	req, err := http.NewRequest("POST", requestUrl, strings.NewReader(DataUrlVal.Encode()))
	if err != nil {
		return
	}
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8")
	req.Header.Add("User-Agent", "MiFit/4.6.0 (iPhone; iOS 14.0.1; Scale/2.00)")
	resp, err := reqClient.Do(req)
	if err != nil {
		return
	}
	_, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	locationUrl, err := resp.Location()
	if err != nil {
		return
	}
	code = locationUrl.Query().Get("access")
	return
}

// login 登录获取token 和user_id
func login(user, password, code string) (loginRes LoginResult, err error) {
	requestUrl := "https://account.huami.com/v2/client/login"
	DataUrlVal := url.Values{}
	DataUrlVal.Add("app_name", "com.xiaomi.hm.health")
	DataUrlVal.Add("app_version", "4.6.0")
	DataUrlVal.Add("code", code)
	DataUrlVal.Add("country_code", "CN")
	DataUrlVal.Add("device_id", "2C8B4939-0CCD-4E94-8CBA-CB8EA6E613A1")
	DataUrlVal.Add("device_model", "phone")
	DataUrlVal.Add("grant_type", "access_token")
	DataUrlVal.Add("third_name", "huami_phone")
	req, err := http.NewRequest("POST", requestUrl, strings.NewReader(DataUrlVal.Encode()))
	if err != nil {
		return
	}
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8")
	req.Header.Add("User-Agent", "MiFit/4.6.0 (iPhone; iOS 14.0.1; Scale/2.00)")
	resp, err := reqClient.Do(req)
	if err != nil {
		return
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}
	err = json.Unmarshal(body, &loginRes)
	return
}

// getAppToken 获取app_token
func getAppToken(token string) (appTokenRes GetAppToken, err error) {
	resp, err := reqClient.Get("https://account-cn.huami.com/v1/client/app_tokens?app_name=com.xiaomi.hm.health&dn=api-user.huami.com%2Capi-mifit.huami.com%2Capp-analytics.huami.com&login_token=" + token)
	if err != nil {
		return
	}
	body, err := ioutil.ReadAll(resp.Body)

	if err != nil {
		return
	}

	err = json.Unmarshal(body, &appTokenRes)
	return
}

// getReplacedData 替换步数和日期
func getReplacedData(step string) (tempJsonStr string) {
	tempJsonStr = SourceData
	today := fmt.Sprintf("%d-%d-%d", time.Now().Local().Year(), time.Now().Local().Month(), time.Now().Local().Day())
	tempJsonStr = strings.ReplaceAll(tempJsonStr, "2020-08-14", today)
	tempJsonStr = strings.ReplaceAll(tempJsonStr, "125", step)
	return
}

// pushStepData 推送步数数据
func pushStepData(appToken, userId, step string) (err error) {
	requestUrl := fmt.Sprintf("%s%d", "https://api-mifit-cn.huami.com/v1/data/band_data.json?&t=", time.Now().UnixNano()/1e6)
	replacedData := getReplacedData(step)
	// 这里的参数必须保持顺序 所以使用str拼接
	data := fmt.Sprintf("userid=%s&last_sync_data_time=1597306380&device_type=0&last_deviceid=DA932FFFFE8816E7&data_json=%s", userId, replacedData)
	req, err := http.NewRequest("POST", requestUrl, strings.NewReader(data))
	if err != nil {
		return
	}
	req.Header.Add("apptoken", appToken)
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	resp, err := reqClient.Do(req)

	if err != nil {
		return
	}
	res, err := ioutil.ReadAll(resp.Body)
	log.Default().Println("推送步数信息: " + string(res))
	return
}

// pushServerChanMessage 推送消息
func pushServerChanMessage(title, msg, serverChan string) (err error) {
	v := url.Values{}
	v.Add("title", title)
	v.Add("desp", msg)

	pushUrl := fmt.Sprintf("https://sctapi.ftqq.com/%s.send?%s", serverChan, v.Encode())
	resp, err := reqClient.Get(pushUrl)
	if err != nil {
		return
	}
	s, _ := ioutil.ReadAll(resp.Body)
	log.Default().Println("微信推送消息结果: " + string(s))
	return

}

// 程序入口
func main() {

	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	reqClient = &http.Client{
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			return http.ErrUseLastResponse /* 不进入重定向 */
		},
		Transport: tr,
	}
	serverChan := flag.String("sc", "default", "serverChan 推送密钥")
	userNameListStr := flag.String("user", "", "user list account1#account2")
	passwdStr := flag.String("password", "", "password list password1#password2")
	stepStr := flag.String("step", "", "step list password1#password2")
	flag.Parse()
	serverChanList := strings.Split(*serverChan, "#")
	userAccountList := strings.Split(*userNameListStr, "#")
	passwordList := strings.Split(*passwdStr, "#")
	stepList := strings.Split(*stepStr, "#")
	currentPassword := passwordList[0]
	currentStep := stepList[0]
	currentServerChan := serverChanList[0]
	for i, user := range userAccountList {
		if i+1 <= len(passwordList) {
			currentPassword = passwordList[i]
		}
		if i+1 <= len(stepList) && currentStep != "" {
			currentStep = stepList[i]
		} else {
			currentStep = getRandStr(17000, 26000)
		}
		if i+1 <= len(serverChanList) {
			currentServerChan = serverChanList[i]
		}
		code, err := getUserAuthCode(user, currentPassword)
		if err != nil {
			log.Default().Println(err.Error())
			continue
		}
		lr, err := login(user, currentPassword, code)
		if err != nil {
			log.Default().Println(err.Error())
			continue
		}
		getAppTokenRes, err := getAppToken(lr.TokenInfo.LoginToken)
		if err != nil {
			log.Default().Println(err.Error())
			continue
		}
		err = pushStepData(getAppTokenRes.TokenInfo.AppToken, lr.TokenInfo.UserId, currentStep)
		if err != nil {
			log.Default().Println(err.Error())
			continue
		}
		log.Println(fmt.Sprintf("推送步数成功 %s %s", user, currentStep))
		if currentServerChan != "" {
			err = pushServerChanMessage("小米运动步数修改通知", fmt.Sprintf("推送步数成功 %s %s", user, currentStep), currentServerChan)
			if err != nil {
				log.Default().Println(err.Error())
				continue
			}
		}
	}
}
